package com.mytime.resources;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.restlet.data.CharacterSet;
import org.restlet.ext.json.JsonRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Put;
import org.restlet.resource.ServerResource;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.mytime.database.Event;
import com.mytime.database.PMF;
import com.mytime.database.Timetable;
import com.mytime.database.Venue;
import com.mytime.util.Time;

public class VenueResource extends ServerResource {

	private String venue_id;
	
	@Override
	public void doInit() {
		this.venue_id = (String) getRequestAttributes().get("venue_id");
	}

    /**
     * Handles a GET HTTP request.
     * Returns all information about a venue including its events. 
     * @param entity
     */
	@Get
	public Representation getvenue() throws Exception {
		JSONObject result = new JSONObject();
				
		try {
			//Gets venue.
			Venue venue = getVenueInfo();
			if (venue == null) {
				result.put("error", "Venue doesn't exists.");
				return getRepresentation(result);
			}
			
			//Add event info to result object.
			result = venue.toJSON();
			
			//Gets events from venue and adds to result object.
			JSONArray eventsList = getVenueEvents(venue);					
			result.put("events", eventsList);
			
		} catch (Exception e) {
			result.put("error", e.getMessage());
		}
		
		return getRepresentation(result);
	}

    /**
     * Handles a PUT HTTP request.
     * Updates a venue. 
     * @param entity
     */
	@Put
	public Representation updateVenue(Representation entity) throws Exception {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		JsonRepresentation rep = new JsonRepresentation(entity);
		JSONObject result = new JSONObject();
		
        try {
        	//Gets data.
			JSONObject data = rep.getJsonObject();
			
			//Get venue
			Venue venue = getVenueInfo();
			if (venue == null) {
				result.put("error", "Venue doesn't exists.");
				return getRepresentation(result);
			}

			//Name is the same, so the object will be updated
			if (!data.has("name") || venue.getName().equals(data.getString("name"))) {
				venue.updateValues(data);
				pm.makePersistent(venue);
					
			//TODO: when name changes, create a new object.
			} else {
				//Creates new Venue
				/*Venue newVenue = new Venue(data);
				
				//Copies events
				newVenue.setEventsList(venue.getEventsList());
				
				//Saves new Venue
				pm.makePersistent(newVenue);
				
				//Updates Timetable
				List<Timetable> timetable = (List<Timetable>) getVenueTimetable(pm, venue);   
				for (Timetable t: timetable) {
					t.setVenueId(newVenue.getEncodedKey());
					pm.makePersistent(t);
				}
				
				//Updates Venues' events
				 
				
				//Deletes the old Venue
				pm.deletePersistent(venue);*/
			}
			
			result.put("updated", venue_id);
			
        } catch (JSONException e) {
			e.printStackTrace();
			result.put("error", e.getMessage());
			
		} finally {
    		pm.close();
    	}
        
        return getRepresentation(result);
	}
	
    /**
     * Handles a DELETE HTTP request.
     * Delete a venue.  
     * @param entity
     */
	@Delete
	public Representation deleteVenue(Representation entity) throws Exception {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		JSONObject result = new JSONObject();
		Transaction tx = pm.currentTransaction();

		try {
			//Gets venue.
			Venue venue = getVenueInfo();
			if (venue == null) {
				result.put("error", "Venue doesn't exists.");
				return getRepresentation(result);
			}
			
			//Delete Timetable entries for this Venue
			List<Timetable> timetable = (List<Timetable>) getVenueTimetable(pm, venue);		
			for (Timetable time: timetable) {
				System.out.println(time.getStartTime());
				//Delete time entry
				tx.begin();
					pm.deletePersistent(time);
				tx.commit();
			}
		
			//Deletes relationship between this venue and its events
			HashSet<Key> eventsKeys = (HashSet<Key>) venue.getEventsList();
			Iterator<Key> itr = eventsKeys.iterator();
			while (itr.hasNext()) {
				Key eventKey = itr.next();
				Event e = pm.getObjectById(Event.class, eventKey);

				//Delete relationships
				e.removeVenue(venue.getKey());
				venue.removeEvent(e.getKey());
				
				pm.makePersistent(e);
				pm.makePersistent(venue);
			}
		
			pm.deletePersistent(venue);

		} finally {
			if (tx.isActive()) {
				tx.rollback();
            }
		}
		
		return getRepresentation(result);
	}


	/**
	 * Returns the JSON representation for a given JSON object.
	 * */
    private Representation getRepresentation(JSONObject jsonObject) {
    	JsonRepresentation jr = new JsonRepresentation(jsonObject);
    	jr.setCharacterSet(CharacterSet.UTF_8);
    	return jr;
    }    
    
    /**
     * Gets all information about the venue.
     * **/
    private Venue getVenueInfo() {
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	Venue venue = null;
		
    	//Search for venue 
    	Query queryVenue = pm.newQuery(Venue.class);
		queryVenue.setFilter("strKey == \'" + venue_id + "\'");
		
		@SuppressWarnings("unchecked")
		List<Venue> venueList = (List<Venue>) queryVenue.execute(); 
		
		//Venue doesn't exist. 
		if (!venueList.iterator().hasNext()) {
			return null;
		}

		//Venue does exists and it will be retrieved.
		venue = venueList.iterator().next();
		
		queryVenue.closeAll();
		pm.close();
		return venue;
    }

    /**
     * Gets all events that belong to this venue.
     * **/
    private JSONArray getVenueEvents(Venue v) {
    	PersistenceManager pm = PMF.get().getPersistenceManager();
    	
		HashSet<Key> eventsKeys = (HashSet<Key>) v.getEventsList();
    	JSONArray eventsFromVenue = new JSONArray();
    	
    	try {
	    	for (Key k: eventsKeys) {
	    		Event e = pm.getObjectById(Event.class, k);
	    		JSONObject eventObject = e.toJSON();
	    		
	    		//Get Showtimes
	    		Query showtimesQuery = pm.newQuery(Timetable.class);
	    		String filter = "eventId == \'" + KeyFactory.keyToString(e.getKey()) + "\'";
	    		filter += " && venueId == \'" + KeyFactory.keyToString(v.getKey()) + "\'";
	    		showtimesQuery.setFilter(filter);
	    		
	    		@SuppressWarnings("unchecked")
				List<Timetable> times = (List<Timetable>) showtimesQuery.execute();
	    		JSONArray showtimes = new JSONArray();
	    		for (Timetable time: times) {
	    			JSONObject timeObj = new JSONObject();
	    			
	    			Time startTime = new Time(time.getStartTime());
	    			Time endTime = new Time(time.getEndTime());
	    			timeObj.put("startTime", startTime.toString());
	    			timeObj.put("endTime", endTime.toString());
	    			
	    			showtimes.put(timeObj);
	    		}
	    		
	    		eventObject.put("showtimes", showtimes);
	    		eventsFromVenue.put(eventObject);
	    	}
	    	
    	} catch (JSONException e1) {
			e1.printStackTrace();
		}
    	
		pm.close();
		return eventsFromVenue;
    }
    
    private List<Timetable> getVenueTimetable(PersistenceManager pm, Venue venue) {
        Query venueTimetable = pm.newQuery(Timetable.class);
    	venueTimetable.setFilter("venueId == \'" + venue.getEncodedKey() + "\'");
    	
    	@SuppressWarnings("unchecked")
    	List<Timetable> timetable = (List<Timetable>) venueTimetable.execute();
    	
    	return timetable;
    }
    
}
